package com.ruiysoft.service.logistics.impl;


import com.alibaba.fastjson.JSON;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ruiysoft.controller.logistics.vo.ExpressInfo;
import com.ruiysoft.entity.logistics.*;
import com.ruiysoft.entity.logistics.kuaidi100.Kuaidi100Config;
import com.ruiysoft.pub.core.Result;
import com.ruiysoft.pub.core.ResultCode;
import com.ruiysoft.service.logistics.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service
@SuppressWarnings("unchecked")
public class ExpressServiceImpl implements ILogisticsQueryService, ILogisticsManageService {

    private static Logger logger = LoggerFactory.getLogger(ExpressServiceImpl.class);

    private final static Long OFFICAL_SHOP_ID = 0L;

    private Map<Integer, List<LogisticsShopApiConf>> listMap = new HashMap<>();

    @Autowired
    private ILogisticsCompanyBaseService logisticsCompanyBaseService;

    @Autowired
    private ILogisticsShopApiConfService logisticsShopApiConfService;

    @Autowired
    private ILogisticsFinalStateService logisticsFinalStateService;

    @Autowired
    private ILogisticsFinalDetailService logisticsFinalDetailService;

    @Autowired
    private ILogisticsSubscribeService subscribeService;

    @Autowired
    private ILogisticsSubscribe logisticsSubscribe;

    //@Autowired
    //private LogisticsShopApiConfMapper logisticsShopApiConfMapper;


    @Override
    public boolean validate(Long shopID, String expressNO, String expressCom) throws LogisticsException {
        boolean result = false;
        LogisticsShopApiConf logisticsShopApiConf = getUseableLogisticsApiConf4Shop(shopID);
        if (logisticsShopApiConf != null) {
            Kuaidi100Config kuaidi100Config = JSON.parseObject(logisticsShopApiConf.getApiParameters(), Kuaidi100Config.class);
            IExpressCaller iExpressCaller = ExpressCollerFactory.getInstance().getExpressCaller(logisticsShopApiConf.getApiSupplier());
            Optional<List<? extends APIExpressCompany>> list = iExpressCaller.queryExpressCompByExpressNO(kuaidi100Config, expressNO);
            if (list.isPresent()) {
                if (list.get().stream().filter(c -> ((APIExpressCompany) c).getComCode().equals(expressCom)).count() > 0) {
                    result = true;
                }
            }
        }
        return result;
    }

    @Override
    public Result<Void> subscribe(Long shopID, String subscriber, String subscribeID, String expressNO, String expressCom) throws LogisticsException {
        Result<Void> result = new Result<Void>();

        QueryWrapper<LogisticsSubscribe> checkExist = new QueryWrapper<>();
        checkExist.eq("subscribe_id", subscribeID)
                .eq("express_no", expressNO)
                .eq("express_com", expressCom)
                .ne("subscriber", subscriber);
        int existCount = subscribeService.count(checkExist);
        logger.debug("Logistics check exist,count is:{}", existCount);
        if (existCount > 0) {
            result.setBizCode(ResultCode.CODE_COMMON_ERROR);
            result.setBizMessage("系统中存在此物流单号，请填写真实的物流单号。");
            return result;
        }


        QueryWrapper<LogisticsSubscribe> checkRepeat = new QueryWrapper<>();
        checkRepeat.eq("subscribe_id", subscribeID)
                .eq("subscriber", subscriber)
                .eq("express_no", expressNO)
                .eq("express_com", expressCom);


        int count = subscribeService.count(checkRepeat);
        logger.debug("Logistics check repeat,count is:{}", count);
        if (count > 0) {
            result.setBizCode(ResultCode.CODE_SUCCESS);
            result.setBizMessage("不要重复订阅信息");
            return result;
        }
        boolean isExpressChecked = isExpressChecked(shopID, expressNO, expressCom);
        if (isExpressChecked) {
            logger.warn("Express is checked,subscribe may fails,subscriber:{},subscribeID:{},expressNO:{},expressCom:{}", subscriber, subscribeID, expressNO, expressCom);
            result.setBizCode(ResultCode.CODE_COMMON_ERROR);
            result.setBizMessage("此物流单已经被签收，请填写正确物流单号。");
            return result;
        }

        //生成一个订阅信息`
        LogisticsSubscribe subscribe = new LogisticsSubscribe();
        subscribe.setCheckStatus(Boolean.TRUE);
        subscribe.setSubscribeId(subscribeID);
        subscribe.setSubscriber(subscriber);
        subscribe.setExpressNo(expressNO);
        subscribe.setExpressCom(expressCom);
        subscribeService.save(subscribe);

        result.setBizCode(ResultCode.CODE_SUCCESS);
        return result;
    }

    @Override
    public List<LogisticsSubscribe> getSubscribe(String expressNO, String expressCom) {
        QueryWrapper<LogisticsSubscribe> subscribeEntityWrapper = new QueryWrapper<>();
        subscribeEntityWrapper.eq("express_no", expressNO);
        subscribeEntityWrapper.eq("express_com", expressCom);
        return subscribeService.list(subscribeEntityWrapper);
    }

    @Override
    public ExpressInfo revertExpressComByENO(Long shopID, String expressNO) throws LogisticsException {
        ExpressInfo expressInfo = new ExpressInfo();
        expressInfo.setExpressNO(expressNO);
        LogisticsShopApiConf logisticsShopApiConf = getUseableLogisticsApiConf4Shop(shopID);
        if (logisticsShopApiConf != null) {
            IExpressCaller iExpressCaller = ExpressCollerFactory.getInstance().getExpressCaller(logisticsShopApiConf.getApiSupplier());
            Kuaidi100Config kuaidi100Config = JSON.parseObject(logisticsShopApiConf.getApiParameters(), Kuaidi100Config.class);

            Optional<List<? extends APIExpressCompany>> optionalList = iExpressCaller.queryExpressCompByExpressNO(kuaidi100Config, expressNO);
            if (optionalList.isPresent()) {
                List<? extends APIExpressCompany> list = optionalList.get();
                for (APIExpressCompany company : list) {
                    String expressCom = company.getComCode();
                    String expressComName = company.getComCode();
                    LogisticsCompanyBase companyBase = queryCompanyBase(company.getComCode());
                    if (companyBase != null) {
                        expressComName = companyBase.getUnionName();
                    }
                    expressInfo.addExpressCompany(expressCom, expressComName);
                }
            }
        }
        return expressInfo;
    }


    @Override
    public boolean isExpressChecked(Long shopID, String expressNO, String expressCom) throws LogisticsException {
        return Optional.ofNullable(queryLogisticsMessage(shopID, expressNO, expressCom)).map(c -> c.getIscheck()).orElse(Boolean.FALSE);
    }

    @Override
    public APILogisticsMessage queryLogisticsMessage(Long shopID, String expressNO) throws LogisticsException {
        //TODO 前半部分的代码与 queryLogisticsMessage(Long shopID, String expressNO, String expressCom) throws LogisticsException 一毛一样，是不是需要优化呢？？！！
        Date date = DateUtils.addMinutes(new Date(), -15);//为签收的，15分钟内采用数据库中的物流信息
        Optional<LogisticsFinalState> optionalFinalState = queryFinalStateFromLocal(shopID, expressNO);
        Optional<LogisticsFinalState> checkedFinalState = optionalFinalState.filter(c -> c.getCheck() || c.getGmtModified().after(date));

        if (checkedFinalState.isPresent()) {
            Optional<LogisticsFinalDetail> checkedFinalDetail = queryFinalDetailFraomLocal(checkedFinalState.get().getId());
            return toAPILogisticsMessage(checkedFinalState, checkedFinalDetail).get();
        }

        APILogisticsMessage message = null;
        LogisticsShopApiConf logisticsShopApiConf = getUseableLogisticsApiConf4Shop(shopID);
        if (logisticsShopApiConf != null) {
            IExpressCaller iExpressCaller = ExpressCollerFactory.getInstance().getExpressCaller(logisticsShopApiConf.getApiSupplier());
            Kuaidi100Config kuaidi100Config = JSON.parseObject(logisticsShopApiConf.getApiParameters(), Kuaidi100Config.class);
            Optional<List<? extends APIExpressCompany>> optionalList = iExpressCaller.queryExpressCompByExpressNO(kuaidi100Config, expressNO);
            if (optionalList.isPresent()) {
                List<? extends APIExpressCompany> list = optionalList.get();
                for (APIExpressCompany company : list) {
                    message = queryLogisticsMessage(shopID, expressNO, company.getComCode());
                    if (message != null) {
                        break;
                    }
                }
            }
        }
        return message;
    }

    @Override
    @Transactional
    public APILogisticsMessage queryLogisticsMessage(Long shopID, String expressNO, String expressCom) throws LogisticsException {
        Date date = DateUtils.addMinutes(new Date(), -15);//为签收的，15分钟内采用数据库中的物流信息
        Optional<LogisticsFinalState> optionalFinalState = queryFinalStateFromLocal(shopID, expressNO);
        Optional<LogisticsFinalState> checkedFinalState = optionalFinalState.filter(c -> c.getCheck() || c.getGmtModified().after(date));

        if (checkedFinalState.isPresent()) {
            Optional<LogisticsFinalDetail> checkedFinalDetail = queryFinalDetailFraomLocal(checkedFinalState.get().getId());
            return toAPILogisticsMessage(checkedFinalState, checkedFinalDetail).get();
        }

        LogisticsShopApiConf logisticsShopApiConf = getUseableLogisticsApiConf4Shop(shopID);
        APILogisticsMessage logisticsMessage = queryLogisticsMessageRemote(shopID, logisticsShopApiConf, expressNO, expressCom);
        if (logisticsMessage != null) {
            LogisticsFinalState finalState = optionalFinalState.orElse(new LogisticsFinalState());
            finalState.setShopId(shopID);
            finalState.setApiSupplier(logisticsShopApiConf.getApiSupplier());
            saveToLocal(finalState, logisticsMessage);
        }
        return logisticsMessage;
    }


    private APILogisticsMessage queryLogisticsMessageRemote(Long shopID, LogisticsShopApiConf logisticsShopApiConf, String expressNO, String expressCom) throws LogisticsException {
        APILogisticsMessage logisticsMessage = null;
        if (logisticsShopApiConf != null) {
            IExpressCaller iExpressCaller = ExpressCollerFactory.getInstance().getExpressCaller(logisticsShopApiConf.getApiSupplier());
            //Kuaidi100Config kuaidi100Config = JSON.parseObject(logisticsShopApiConf.getApiParameters(), Kuaidi100Config.class);
            ILogisticsConfig logisticsConfig = iExpressCaller.parseConfig(logisticsShopApiConf.getApiParameters());
            Optional<? extends APILogisticsMessage> apiLogisticsMessage = iExpressCaller.queryExpress(logisticsConfig, expressNO, expressCom);
            if (apiLogisticsMessage.filter(c -> c.isResult()).isPresent()) {
                ///apiLogisticsMessage.map(c->c.setComCode())
                logisticsMessage = apiLogisticsMessage.get();
                String unionName = Optional.ofNullable(
                        queryCompanyBase(logisticsMessage.getComCode())
                ).map(c -> c.getUnionName())
                        .orElse(logisticsMessage.getComCode());
                logisticsMessage.setComCompany(unionName);
                //采用队列方式处理远程订阅
                logisticsSubscribe.push2MQ(shopID, logisticsShopApiConf, logisticsMessage.getComCode(), expressNO);
            }
        }
        return logisticsMessage;
    }

    private LogisticsCompanyBase queryCompanyBase(String expressCom) {
        QueryWrapper<LogisticsCompanyBase> wrapper = new QueryWrapper<>();
        wrapper.eq("union_code", expressCom);
        List<LogisticsCompanyBase> list = logisticsCompanyBaseService.list(wrapper);
        if (list.size() > 0) {
            return list.get(0);
        }
        return null;
    }


    /**
     * 获取第一个可以使用的快递配置
     *
     * @param shopID
     * @return
     */
    public LogisticsShopApiConf getUseableLogisticsApiConf4Shop(Long shopID) {
        //缓存：通过mybatis缓存实现,Mapper设置x分钟缓存有效期
        QueryWrapper<LogisticsShopApiConf> q = new QueryWrapper<LogisticsShopApiConf>();
        q.in("shop_id", Arrays.asList(OFFICAL_SHOP_ID, shopID)).orderBy(false, false, "shop_id desc,priority asc");
        List<LogisticsShopApiConf> list = logisticsShopApiConfService.list(q);
        if (list.size() > 0) {
            return list.get(0);
        }
        return null;
    }


    @Override
    public void updateLogistics(String apiSupplier, Long shopID, APILogisticsMessage logisticsMessage) {
        String unionName = Optional.ofNullable(queryCompanyBase(logisticsMessage.getComCode())).map(c -> c.getUnionName()).orElse(null);
        logisticsMessage.setComCompany(unionName);
        Optional<LogisticsFinalState> optionalFinalState = queryFinalStateFromLocal(shopID, logisticsMessage.getExpressNO());
        LogisticsFinalState finalState = optionalFinalState.orElse(new LogisticsFinalState());
        finalState.setShopId(shopID);
        finalState.setApiSupplier(apiSupplier);
        saveToLocal(finalState, logisticsMessage);
    }

    @Override
    public List<LogisticsCompanyBase> queryAllLogisticsCompany() {
        QueryWrapper<LogisticsCompanyBase> companyBaseEntityWrapper = new QueryWrapper<>();
        companyBaseEntityWrapper.orderBy(false, false, "show_order");
        return logisticsCompanyBaseService.list(companyBaseEntityWrapper);
    }

    @Override
    public int saveOrUpdateLogisticsShopApiConf(LogisticsShopApiConf logisticsShopApiConf) {
        logisticsShopApiConfService.saveOrUpdate(logisticsShopApiConf);
        return logisticsShopApiConf.getId();
    }


    private Optional<LogisticsFinalState> queryFinalStateFromLocal(Long shopID, String expressNO) {
        Date date = DateUtils.addMinutes(new Date(), -15);//为签收的，15分钟内采用数据库中的物流信息
        QueryWrapper<LogisticsFinalState> finalStateEntityWrapper = new QueryWrapper<>();
        finalStateEntityWrapper.eq("express_no", expressNO);
        finalStateEntityWrapper.eq("shop_id", shopID);
        List<LogisticsFinalState> logisticsFinalStates = logisticsFinalStateService.list(finalStateEntityWrapper);
        return logisticsFinalStates.stream().findFirst();
    }


    private Optional<LogisticsFinalDetail> queryFinalDetailFraomLocal(Integer id) {
        return Optional.ofNullable(logisticsFinalDetailService.getById(id));
        //return toAPILogisticsMessage(finalState,finalDetail);
    }

    @Async
    private void saveToLocal(LogisticsFinalState finalState, APILogisticsMessage apiLogisticsMessage) {
        finalState.setUnionCode(apiLogisticsMessage.getComCode());
        finalState.setExpressNo(apiLogisticsMessage.getExpressNO());
        finalState.setCheck(apiLogisticsMessage.getIscheck());
        finalState.setState(apiLogisticsMessage.getState());
        finalState.setStatus(apiLogisticsMessage.getStatus());
        if (finalState.getGmtCreate() == null) {
            finalState.setGmtCreate(new Date());
            finalState.setGmtModified(finalState.getGmtCreate());
        } else {
            finalState.setGmtModified(new Date());
        }
        if (logisticsFinalStateService.saveOrUpdate(finalState)) {
            LogisticsFinalDetail finalDetail = new LogisticsFinalDetail();
            finalDetail.setId(finalState.getId());
            if (apiLogisticsMessage.getData() != null && apiLogisticsMessage.getData().length > 0) {
                finalDetail.setContent(JSON.toJSONString(apiLogisticsMessage.getData()));
            }
            finalDetail.setGmtModified(new Date());
            logisticsFinalDetailService.saveOrUpdate(finalDetail);
        }
    }

    private Optional<APILogisticsMessage> toAPILogisticsMessage(Optional<LogisticsFinalState> finalState, Optional<LogisticsFinalDetail> finalDetail) {
        if (!finalState.isPresent()) {
            return Optional.empty();
        }
        LogisticsFinalState state = finalState.get();
        APILogisticsMessage logisticsMessage = new APILogisticsMessage();
        logisticsMessage.setComCode(state.getUnionCode());
        String unionName = Optional.ofNullable(queryCompanyBase(state.getUnionCode())).map(c -> c.getUnionName()).orElse(null);
        logisticsMessage.setComCompany(unionName);
        logisticsMessage.setExpressNO(state.getExpressNo());
        logisticsMessage.setIscheck(state.getCheck());
        logisticsMessage.setState(state.getState());
        logisticsMessage.setStatus(state.getStatus());
        logisticsMessage.setNu(logisticsMessage.getExpressNO());
        String content = finalDetail.map(c -> c.getContent()).orElse(null);
        if (StringUtils.isNotEmpty(content)) {
            List<APILogisticsMessage.LogisticsMessageDetail> messageDetail = JSON.parseArray(content, APILogisticsMessage.LogisticsMessageDetail.class);
            logisticsMessage.setData(messageDetail.toArray(new APILogisticsMessage.LogisticsMessageDetail[messageDetail.size()]));
        }
        return Optional.of(logisticsMessage);
    }


}
